﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using System.Windows.Forms;
using System.Threading;

namespace binary
{
    public class MyDBF
    {
        private int _liczba_rekordow = 0;
        private int _odczytano = 0;
        private int typ, _bledy=0;
        private Int16 _pozycja_pierwszego;
        private List<MyKolumna> _kolumny = new List<MyKolumna>();
        private int _dlugosc_rekordu;
        private string _plik;
        private DataTable _dane = new DataTable();
        string _nazwa_bazy = "";
        string sciezka = "";
        private bool _odczytano_naglowek = false;
        public List<MyBlad> ListaBledy = new List<MyBlad>();
        public bool czy_czytac = true, koniec = false;
        public delegate void Proces();
        public int ograniczenie_pamieci = 10;
        public long pamiec_na_starcie;
        public long FileSize = 0;



        public int LiczbaRekordow
        {
            get { return _liczba_rekordow; }
            set { _liczba_rekordow = value; }
        }


        public int Odczytano
        {
            get { return _odczytano; }
            set
            {
                _odczytano = value;   
            }
        }

        public bool Odczytano_Naglowek
        {
            get { return _odczytano_naglowek; }
        }

        public int Bledy
        {
            get { return _bledy; }
        }


        public Int16 PozycjaPierwszego
        {
            get { return _pozycja_pierwszego; }
            set { _pozycja_pierwszego = value; }
        }

        public List<MyKolumna> Kolumny 
        {
            get { return _kolumny; }
        }

        public int DlugoscRekordu
        {
            get { return _dlugosc_rekordu; }
            set { _dlugosc_rekordu = value; }
        }

        public string Plik
        {
            set { _plik = value; }
            get { return _plik; }
        }

        public String NazwaBazy
        {
            get { return _nazwa_bazy; }
            set { _nazwa_bazy = value; }
        }



        public MyDBF(string plik, string nazwa_bazy)
        {
            this.Plik = plik;
            this.sciezka = Path.GetDirectoryName(this.Plik);
            this.FileSize = new FileInfo(this.Plik).Length;
            this.NazwaBazy = nazwa_bazy;
            Thread t = new Thread(new ThreadStart(pamiec));
            t.Start();
            using (BinaryReader b = new BinaryReader(File.Open(plik, FileMode.Open, FileAccess.Read)))
            {
                try
                {
                    byte[] tab = new byte[32];
                    byte znak_konca = 0;
                    
                    b.Read(tab, 0, 32);
                    this.typ = tab[0];
                    this.LiczbaRekordow = BitConverter.ToInt32(tab, 4); //odczyt liczby rekordów
                    this.PozycjaPierwszego = BitConverter.ToInt16(tab, 8); //odczyt liczby rekordów
                    this.DlugoscRekordu = BitConverter.ToInt16(tab, 10); //odczyt liczby rekordów
                    while (znak_konca != 13)
                    {                   
                        b.Read(tab, 0, 32); //odczyt informacji o klumnie
                        MyKolumna kol = new MyKolumna();
                        kol.Nazwa = Encoding.ASCII.GetString(bajty(tab, 0, 10)).Replace("\0",""); //zapisanie nazwy
                        kol.Typ = Encoding.ASCII.GetChars(bajty(tab, 11, 1)); //typ pola
                        kol.Przesuniecie = BitConverter.ToInt32(tab, 12);
                        kol.Dlugosc = tab[16]; // dlugosc
                        kol.DlugoscDecimal = tab[17]; //decimal places
                        kol.Flaga = tab[18].ToString(); //flaga
                        string strona = tab[29].ToString();
                        znak_konca = b.ReadByte();
                        b.BaseStream.Seek(-1, SeekOrigin.Current);
                        kol.Linia = bajty2string(tab);
                        _kolumny.Add(kol);
                    }
                    UstalDlugoscRekordu();
                    this._odczytano_naglowek = true;
                }
                catch(Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
                b.Close();
                
            }
        }

        private void pamiec()
        {
            while (koniec == false)
            {
                Thread.Sleep(100);
                if ((GC.GetTotalMemory(true)) / 1024 / 1024 > pamiec_na_starcie + ograniczenie_pamieci)
                {
                    this.czy_czytac = false;
                }
                else
                {
                    this.czy_czytac = true;
                }
            }
        }


        private string bajty2string(byte[] tablica)
        {
            string output = "";
            for(int i=0; i<=tablica.Length-1; i++)
            {
                output += tablica[i];
            }
            return output;
        }

        private byte[] bajty(byte[] tablica, int index, int liczba)
        {
            byte[] tab = new byte[liczba];
            int licznik = 0;
            for (int i = index; i < index + liczba; i++)
            {
                tab[licznik] =  tablica[i];
                licznik++;
            }
            return tab;
        }


        private void UstalDlugoscRekordu()
        {
            int dlugosc = 0;
            foreach (MyKolumna kol in Kolumny)
            {
                dlugosc += kol.Dlugosc;
            }
            this.DlugoscRekordu = dlugosc;
        }


        public void PobierzDane(DataTable Dane)
        {

            Dane.Columns.Clear();
            Type typ;
            foreach (MyKolumna col in Kolumny)
            {
                if(col.Typ[0]=='N' && col.DlugoscDecimal==0 && col.Dlugosc<10) typ = System.Type.GetType("System.Int32");
                if (col.Typ[0] == 'N' && col.DlugoscDecimal == 0 && col.Dlugosc >= 10) typ = System.Type.GetType("System.Int64");
                else if(col.Typ[0]=='N' && col.DlugoscDecimal>0) typ = System.Type.GetType("System.Decimal");
                else if (col.Typ[0] == 'M') typ = System.Type.GetType("System.String");
                else if (col.Typ[0] == 'D') typ = System.Type.GetType("System.String");
                else if (col.Typ[0] == 'L' ) typ = System.Type.GetType("System.Boolean");
                else if (col.Typ[0] == 'C') typ = System.Type.GetType("System.String");
                else if (col.Typ[0] == 'T') typ = System.Type.GetType("System.String");
                else if (col.Typ[0] == 'I') typ = System.Type.GetType("System.Int32");
                else typ = System.Type.GetType("System.String");
                Dane.Columns.Add(col.Nazwa,typ);
                typ = null;
            }
            GC.Collect();

            pamiec_na_starcie = GC.GetTotalMemory(true) / 1024 / 1024;

            using (BinaryReader b = new BinaryReader(File.Open(this.Plik, FileMode.Open, FileAccess.Read)))
            {
                MyMemo mem = new MyMemo();
                long rozmiar = new FileInfo(this.Plik).Length;
                int iteracja = 0;
                    int i = 0;
                    b.BaseStream.Seek(this.PozycjaPierwszego, SeekOrigin.Begin);
                    while (b.BaseStream.Position < rozmiar - 1)
                    {
                        if (czy_czytac)
                        {
                            iteracja++;
                            i++;
                            byte status = b.ReadByte();
                            DataRow dr = Dane.NewRow();
                            foreach (MyKolumna col in Kolumny)
                            {
                                if (col.Typ[0].ToString() == "M")
                                {
                                    try
                                    {
                                        int linia;
                                        string tekst;
                                        if (this.typ == 48 || this.typ == 49 || this.typ == 50)
                                        {
                                            linia = b.ReadInt32();
                                            tekst = mem.getTekst2(this.sciezka + @"\" + this.NazwaBazy + ".fpt", linia, 1);
                                            dr[col.Nazwa] = tekst;
                                        }
                                        else
                                        {
                                            string slinia = Encoding.GetEncoding(852).GetString(b.ReadBytes(col.Dlugosc)).Replace(" ", "");
                                            if (slinia.Length > 0)
                                            {
                                                linia = int.Parse(slinia);
                                                tekst = mem.getTekst2(this.sciezka + @"\" + this.NazwaBazy + ".fpt", linia, 0);
                                                dr[col.Nazwa] = tekst;
                                            }
                                            else
                                            {
                                                dr[col.Nazwa] = DBNull.Value;
                                            }
                                        }
                                        tekst = null;

                                    }
                                    catch (Exception ex)
                                    {
                                        MyBlad err = new MyBlad(this.NazwaBazy, col.Nazwa, this.Odczytano + 1, "Błąd odczytu pola memo: " + ex.Message);
                                        this.ListaBledy.Add(err);
                                        err = null;
                                        dr[col.Nazwa] = DBNull.Value;
                                    }
                                }
                                else if (col.Typ[0] == 'D')
                                {
                                    string wartosc = Encoding.Default.GetString(b.ReadBytes(col.Dlugosc)).Trim();
                                    if (wartosc.Length > 0)
                                    {
                                        try
                                        {
                                            string data = wartosc.Insert(6, "-").Insert(4, "-");
                                            if (int.Parse(data.Substring(0, 4)) > 1800)
                                            {
                                                dr[col.Nazwa] = data;    
                                            }
                                            else
                                            {
                                                MyBlad err = new MyBlad(this.NazwaBazy, col.Nazwa, this.Odczytano + 1, "Błędna data - mniejsza od 1800 roku");
                                                this.ListaBledy.Add(err);
                                                err = null;
                                            }
                                            data = null;
                                        }
                                        catch (Exception ex)
                                        {
                                            dr[col.Nazwa] = DBNull.Value;
                                            MyBlad err = new MyBlad(this.NazwaBazy, col.Nazwa, this.Odczytano + 1, ex.Message);
                                            err = null;
                                            this.ListaBledy.Add(err);
                                        }
                                    }
                                    else
                                    {
                                        dr[col.Nazwa] = DBNull.Value;
                                    }
                                    wartosc = null;

                                }
                                else if (col.Typ[0] == 'N' && col.DlugoscDecimal == 0)
                                {
                                    try
                                    {
                                        string wartosc = Encoding.Default.GetString(b.ReadBytes(col.Dlugosc)).Trim();
                                        if (wartosc.Length > 0 && wartosc.Length < 10) dr[col.Nazwa] = int.Parse(wartosc);
                                        else if (wartosc.Length >= 10) dr[col.Nazwa] = Int64.Parse(wartosc);
                                        else dr[col.Nazwa] = DBNull.Value;
                                        wartosc = null;
                                    }
                                    catch (Exception ex)
                                    {
                                        MyBlad err = new MyBlad(this.NazwaBazy, col.Nazwa, this.Odczytano + 1, ex.Message);
                                        this.ListaBledy.Add(err);
                                        err = null;
                                        dr[col.Nazwa] = DBNull.Value;
                                    }
                                }
                                else if (col.Typ[0] == 'N' && col.DlugoscDecimal > 0)
                                {
                                    try
                                    {
                                        string wartosc = Encoding.Default.GetString(b.ReadBytes(col.Dlugosc)).Trim().Replace(".", ",");
                                        if (wartosc.Length > 0)
                                            dr[col.Nazwa] = decimal.Parse(wartosc);
                                        else dr[col.Nazwa] = DBNull.Value;
                                        wartosc = null;
                                    }
                                    catch (Exception ex)
                                    {
                                        MyBlad err = new MyBlad(this.NazwaBazy, col.Nazwa, this.Odczytano + 1, ex.Message);
                                        this.ListaBledy.Add(err);
                                        err = null;
                                        dr[col.Nazwa] = DBNull.Value;
                                    }
                                }
                                else if (col.Typ[0] == 'L')
                                {
                                    try
                                    {
                                        string wartosc = Encoding.Default.GetString(b.ReadBytes(col.Dlugosc)).Trim();
                                        if (wartosc == "T") dr[col.Nazwa] = true;
                                        else if (wartosc.Length == 0) dr[col.Nazwa] = DBNull.Value;
                                        else dr[col.Nazwa] = false;
                                        wartosc = null;
                                    }
                                    catch (Exception ex)
                                    {
                                        MyBlad err = new MyBlad(this.NazwaBazy, col.Nazwa, this.Odczytano + 1, ex.Message);
                                        this.ListaBledy.Add(err);
                                        err = null;
                                        dr[col.Nazwa] = DBNull.Value;
                                    }
                                }
                                else if (col.Typ[0] == 'T')
                                {
                                    try
                                    {
                                        byte[] data = new byte[4];
                                        byte[] czas = new byte[4];

                                        data = b.ReadBytes(4);
                                        czas = b.ReadBytes(4);

                                        int idata = BitConverter.ToInt32(data, 0);
                                        int iczas = BitConverter.ToInt32(czas, 0);

                                        DateTime d = new DateTime(1900, 1, 1);
                                        d = d.AddDays(idata - 2415021);
                                        d = d.AddMilliseconds(iczas);

                                        dr[col.Nazwa] = d;
                                        data = null;
                                        czas = null;

                                    }
                                    catch (Exception ex)
                                    {
                                        MyBlad err = new MyBlad(this.NazwaBazy, col.Nazwa, this.Odczytano + 1, ex.Message);
                                        this.ListaBledy.Add(err);
                                        err = null;
                                        dr[col.Nazwa] = DBNull.Value;
                                    }
                                }
                                else if (col.Typ[0] == 'I')
                                {
                                    try
                                    {
                                        dr[col.Nazwa] = b.ReadInt32();
                                    }
                                    catch (Exception ex)
                                    {
                                        MyBlad err = new MyBlad(this.NazwaBazy, col.Nazwa, this.Odczytano + 1, ex.Message);
                                        this.ListaBledy.Add(err);
                                        err = null;
                                        dr[col.Nazwa] = DBNull.Value;
                                    }
                                }
                                else
                                {
                                    try
                                    {
                                        string wartosc = Encoding.GetEncoding(852).GetString(b.ReadBytes(col.Dlugosc)).Trim();
                                        if (wartosc.Length > 0)
                                            dr[col.Nazwa] = wartosc;
                                        else
                                            dr[col.Nazwa] = DBNull.Value;
                                        wartosc = null;
                                    }
                                    catch (Exception ex)
                                    {
                                        MyBlad err = new MyBlad(this.NazwaBazy, col.Nazwa, this.Odczytano + 1, ex.Message);
                                        this.ListaBledy.Add(err);
                                        err = null;
                                        dr[col.Nazwa] = DBNull.Value;
                                    }
                                }
                            }

                            if (status != 26) // rekord nieusunięty
                            {
                                this.Odczytano++;
                                Monitor.Enter(Dane);
                                Dane.Rows.Add(dr);
                                Monitor.Exit(Dane);
                            }
                            dr = null;            
                        }
                        else
                        {
                            Thread.Sleep(500);
                        }   
                    }
                GC.Collect();
                b.Close();
                koniec = true;
            }
        }

        private byte[] odwrocBajty(byte[] tab)
        {
            byte[] tab2 = new byte[tab.Length];
            int licznik = 0;
            for (int i = tab.Length-1; i >=0; i--)
            {
                tab2[licznik] = tab[i];
                licznik++;
            }
            return tab2;
        }

        public string BytesToHex(byte[] bytes)
        {
            StringBuilder hexString = new StringBuilder(bytes.Length);
            for (int i = 0; i < bytes.Length; i++)
            {
                hexString.Append(bytes[i].ToString("X2"));
            }
            return hexString.ToString();
        }




    }
}
